//
// Created by 抑~风 on 2022/12/9.
//


#include "semantic_analysis.h"

namespace CWJ{
    namespace SEMA_ANALYSIS {
        TypeAndScopeScanner::AnnotatedTreePtr TypeAndScopeScanner::visit(AnnotatedTreePtr tree) {
            this->at = tree;
            this->visit(tree->getTree());
            return this->at;
        }

        Scope *TypeAndScopeScanner::getCurScope() {
            if (this->sta.empty()) return nullptr;
            else return this->sta.top();
        }

        void TypeAndScopeScanner::pushScope(Scope *scope) {
            auto cur = this->getCurScope();
            scope->setParentScope(cur);
            if (cur) cur->addSubScope(scope);
            this->sta.push(scope);
        }

        void TypeAndScopeScanner::visit(const ResASTNodePtr root) {

            const auto &text = root->getContext();// string

            auto cur = this->getCurScope();
            bool isPop = false;
            bool isPush = false;
            Scope* s ;
            // 当前节点处理
            if (text == "program") {
                if (cur) throw runtime_error("TypeAndScopeScanner::visit error: program must one");
                s = new Scope(root);
                pushScope(s);
                isPop = true;
            }
            if (text == "block") {
                s = new Scope(root);
                isPush = true;
            } else if (text == "functionDeclaration") {
                auto t = new FunctionType(cur, root);
                s = new FunctionScope(root,cur,t);
                cur->addSymbol(t);
//                cout<<"---"<<t->getName()<<" "<<root->getVal()<<endl;
                // 压栈
                // TODO 需要处理函数重名问题
                isPush = true;
            } else if (text == "classDeclaration") {
                auto t = new ClassType(cur, root);
                s = new ClassScope(root,cur,t);

//                cout<<"---"<<t->getName()<<" "<<root->getVal()<<endl;
                //TODO 需要处理类重名问题
                cur->addSymbol(t);
                // 压栈
                isPush = true;
            } else if (text == "forStatement") {
                s = new Scope(root);
                isPush = true;
            }else if(text == "functionBody"){
                if(cur->getType() != ScopeType::FUNCTION){
                    throw runtime_error("TypeAndScopeScanner::visit : functionBody not declaration");
                }
                const auto& tmp = (FunctionScope*)cur;
                tmp->getObject()->setBody(root);
            }

            this->at->addScopeOfNode(root,this->getCurScope());

            if(isPush){
                this->pushScope(s);
                isPop = true;
            }

            for(const auto & c : root->getChilds()){
                this->visit(c);
            }

            if (isPop) {
                this->sta.pop();
            }

        }
    }
}
